package ni;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;

import javax.swing.JOptionPane;

import ni.Server;
import ni.TCPSender;

import org.json.JSONException;

import gui.ConnectedGUI;
import gui.User;
import controller.P2PController;

public class P2PNI {

	private P2PController controller;
	private UDPsender udpsender;
	private final UDPreceiver udpreceiver;
	   private Server server;

	
	private final int port = 1338;
    private final int destPort = 1338;
    private final String broadcastAddress = "255.255.255.255";
    private int numero = 1;
	
	public P2PNI(P2PController controller){
		this.controller = controller;
        this.server = new Server(this, this.port);
        startServer();
        try {
            this.udpsender = new UDPsender(port, destPort);
        } catch (SocketException ex) {
            System.err.println("NI : Error creating udpSender");
            JOptionPane.showMessageDialog(null, "(NI) UDP Error : The port " + port + " is already in use", "Chatsystem : Error", JOptionPane.ERROR_MESSAGE);
            System.exit(5);
        }
        this.udpreceiver = new UDPreceiver(udpsender.getDataSocket(), this);
        this.udpreceiver.start();

	}
	
    private void startServer() {
        this.server.start();
    }
	
	//PERFORMING SENDING SIDE
	
	public void sendHello(String user_name){
		System.out.println("Envoi d' HELLO");
		
		Message jmessage = new Message();
		try {
			jmessage.put("type","hello");
			jmessage.put("userName", user_name);
		} catch (JSONException e) {
			System.out.println("Erreur dans l'envoi du message Hello" );
			e.printStackTrace();
		}
		try {
			udpsender.sendMessage(jmessage, InetAddress.getByName(broadcastAddress));
		} catch (UnknownHostException e) {
			System.out.println("Erreur dans l'envoi du message Hello");
			e.printStackTrace();
		}
	}


	public void sendGoodBye(String user_name) {
		System.out.println("Envoi de GOODBYE");
		Message jmessage = new Message();
		try {
			jmessage.put("type","goodBye");
		} catch (JSONException e) {
			System.out.println("Erreur dans l'envoi du message goodBye" );
			e.printStackTrace();
		}
		try {
			udpsender.sendMessage(jmessage, InetAddress.getByName(broadcastAddress));
		} catch (UnknownHostException e) {
			System.out.println("Erreur dans l'envoi du message goodBye" );
			e.printStackTrace();
		}
	}


	public void sendHelloAck(User remoteUser, String localUser) {
		System.out.println("Envoi de HelloAck");
		Message jmessage = new Message();
		try {
			jmessage.put("type","helloAck");
			jmessage.put("userName", localUser);
		} catch (JSONException e) {
			System.out.println("Erreur dans l'envoi du HelloACK");
			e.printStackTrace();
		}
		
		udpsender.sendMessage(jmessage, remoteUser.getIpAddress());
		
	}
	
	

	public void sendMessage(String messageToSend, User remoteUser) {
		System.out.println("Envoi d'un MESSAGE" + messageToSend);
		Message jmessage = new Message();

		try {
				jmessage.put("type","message");
				jmessage.put("userName", remoteUser.getName());
				jmessage.put("messageData",messageToSend);
				jmessage.put("messageNumber", numero);
			
				controller.processSendmessage(numero, remoteUser.getIpAddress().toString());

				numero++;
				
		} catch (JSONException e) {
			System.out.println("Erreur dans l'envoi du message" + messageToSend);
			e.printStackTrace();
		}
		udpsender.sendMessage(jmessage, remoteUser.getIpAddress());
		
		
	}
	

	private void sendMessageAck(InetAddress ip, int messageNumber) throws UnknownHostException {
		System.out.println("Envoi d'un MESSAGEACK numero   "  + messageNumber);
		Message jmessage = new Message();
		try {
			jmessage.put("type","messageAck");

			jmessage.put("messageNumber", messageNumber);
		} catch (JSONException e) {
			System.out.println("Erreur dans l'envoi du message" );
			e.printStackTrace();
		}
		udpsender.sendMessage(jmessage, ip);
	}

	
	// PROCESSING RECEIVING SIDE
	
	public void handlePacketReceived(Object packet) throws UnknownHostException {
        if (packet instanceof DatagramPacket) {
            System.out.println("NI/HandlePacket : DatagramPacket received");
            DatagramPacket dataPacket = (DatagramPacket) packet;
            String ip = dataPacket.getAddress().getHostAddress();
            System.out.println(ip);
            Message message;
			try {
				message = new Message(new String(dataPacket.getData()));
				String type = (String) message.getType();
	          
	            if (type.equals("message")) {
	            	String messageData = (String) message.getMessageData();
		            int messageNumber = (int) message.getMessageNumber();
	                System.out.println("NI/HandlePacket :  Received packet type is message");
	                controller.processMessage(ip, messageData, messageNumber);
	                sendMessageAck(dataPacket.getAddress(), messageNumber);
	            } else if (type.equals("messageAck")) {
		            int messageNumber = (int) message.getMessageNumber();
	                System.out.println("NI : Received packet type is messageAck");
	                System.out.println("Ack number is : " + messageNumber);
	                controller.processMessageAck(messageNumber);
	            } else if (type.equals("hello")) {
		            String nickname = (String) message.getUserName();
	                System.out.println("NI/HandlePacket : Received packet type is hello");
	                try {
						controller.processHello(new User(nickname, InetAddress.getByName(ip)));
					} catch (UnknownHostException e) {
						e.printStackTrace();
					}
	            } else if (type.equals("helloAck")) {
		            String nickname = (String) message.getUserName();
	                System.out.println("NI/HandlePacket : Received packet type is helloAck");
	                try {
	                	if(!(ip.toString().equals(InetAddress.getLocalHost().getHostAddress().toString()))){
	                		controller.processHelloAck(new User(nickname, InetAddress.getByName(ip)));
	                	}

					} catch (UnknownHostException e) {
						e.printStackTrace();
					}
	            } else if (type.equals("goodBye")) {
	                System.out.println("NI/HandlePacket : Received packet type is goodBye");
	                try {
						controller.processGoodBye(InetAddress.getByName(ip));
					} catch (UnknownHostException e) {
						e.printStackTrace();
					}
	            } else {
	                System.err.println("NI/HandlePacket : Error with the message (don't recognize Message type)");
	            }

	        
			} catch (JSONException e) {
				e.printStackTrace();
			}
            
        } else {
            System.err.println("P2PNI : Message type unknown");
        }
    }

	public void close_sockets(){
		udpreceiver.close_socket_receiver();
		udpsender.close_socket_sender();
		udpreceiver.arret();
	}
	
	
   
    public void processReceivedFile(String resultToReceive) {
        controller.fileReceivedNotification(resultToReceive);
    }

    public void resultFile(String result) {
        controller.processFileTranferResult(result);
    }



	public void sendFile(String filePath, String userIp) {
		System.out.println("NI: Sending : " + filePath + " to " + userIp);
        try {
            TCPSender newSender;
            newSender = new TCPSender(this, filePath, InetAddress.getByName(userIp), destPort);
            newSender.start();
        } catch (UnknownHostException ex) {
            System.out.println("NI : unknown host exception");
        }		
	}
	
}
